package com.jy.framework.base;/*
 * @Author JuiceYan
 * @Description Action公共处理
 * @Date 2018-12-14
 **/

import cn.afterturn.easypoi.entity.vo.NormalExcelConstants;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.view.PoiBaseView;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.google.common.base.CaseFormat;
import com.jy.framework.annotation.DataFilter;
import com.jy.framework.annotation.RequiresAction;
import com.jy.framework.annotation.SysLog;
import com.jy.framework.exception.MyException;
import com.jy.framework.sys.entity.SysUserEntity;
import com.jy.framework.sys.shiro.ShiroUtils;
import com.jy.framework.utils.*;
import com.jy.framework.validator.ValidatorUtils;
import com.jy.web.diy.entity.BaseInfoEntity;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * 添加响应参数设置
 *
 * @param <S>
 * @param <T>
 * @author
 * @ClassName: BaseController
 * @date 2018年8月31日
 */
public class BaseController<S extends IService<T>, T extends BaseEntity> {
    protected Logger log = LoggerFactory.getLogger(getClass());
    public static final int ENTITY_INDEX = 1;
    /**
     * service
     */
    @Autowired
    public S service;

    /**
     * 获取列表
     *
     * @param params
     * @return
     */
    @GetMapping("/list")
    @RequiresAction("list")
    @ApiOperation("分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = Constant.PageParams.PAGE, value = "当前页码，从1开始", paramType = "query", required = true, dataType="int") ,
            @ApiImplicitParam(name = Constant.PageParams.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataType="int") ,
            @ApiImplicitParam(name = Constant.PageParams.SIDX, value = "排序字段", paramType = "query", dataType="String") ,
            @ApiImplicitParam(name = Constant.PageParams.ORDER, value = "排序方式，可选值(asc、desc)", paramType = "query", dataType="String")
    })
//    @DataFilter(subDept = true, user = true)
    public R list(@ApiIgnore @RequestParam Map<String, Object> params, T t) {
        QueryWrapper<T> ew = buildQueryConditions(t);
        buildOrderWrapper(params,ew);

        IPage<T> list = service.page(
                new Query<T>(params).getPage(),
                ew
        );
        PageUtils page = new PageUtils(list);
        return R.ok().put("page", page);
    }

    /**
     * 数据权限过滤列表
     *
     * @param params
     * @return
     */
    @GetMapping("/dataFilterList")
    @RequiresAction("list")
    @ApiOperation("分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = Constant.PageParams.PAGE, value = "当前页码，从1开始", paramType = "query", required = true, dataType="int") ,
            @ApiImplicitParam(name = Constant.PageParams.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataType="int") ,
            @ApiImplicitParam(name = Constant.PageParams.SIDX, value = "排序字段", paramType = "query", dataType="String") ,
            @ApiImplicitParam(name = Constant.PageParams.ORDER, value = "排序方式，可选值(asc、desc)", paramType = "query", dataType="String")
    })
    @DataFilter(subDept = true)
    public R dataFilterList(@ApiIgnore @RequestParam Map<String, Object> params, T t) {
        QueryWrapper<T> ew = buildQueryConditions(t);
        buildOrderWrapper(params,ew);
//      数据权限过滤，修改，需要添加默认字段，creator (数据创建者自己)，dept_id(数据所属部门)
        ew.apply(params.get(Constant.SQL_FILTER) != null, (String)params.get(Constant.SQL_FILTER))
        ;
        IPage<T> list = service.page(
                new Query<T>(params).getPage(),
                ew
        );
        PageUtils page = new PageUtils(list);
        return R.ok().put("page", page);
    }

    /**
     * @Author: JuiceYan
     * @Description: 实体类构建wrapper，数值型eq，字符型 like，其他类型先忽略
     * @param: [t]
     * @return: com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<T>
     * @throws:
     * @Date: 22:44 2018/12/2
     */
    public static  <T extends BaseEntity>  QueryWrapper<T> buildQueryConditions(T t) {
        QueryWrapper<T> ew = new QueryWrapper<>();

        List<String> excludedFields = new ArrayList<>();
        Field[] fields = ReflectUtil.getFieldsDirectly(t.getClass(), true);
        for (Field field : fields) {
//            取出final 字段
            if ((java.lang.reflect.Modifier.isFinal(field.getModifiers()))) {
                continue;
            }
            Object value = null;
            try {
                field.setAccessible(true);
                value = field.get(t);
//                值 null，不作为条件
                if (null == value) {
                    continue;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            String name = field.getName();
            TableField tableField = field.getAnnotation(TableField.class);
//           非数据库字段
            if (null != tableField && !tableField.exist()) {
                continue;
            }
//            其他非条件字段
            if (excludedFields.contains(name)) {
                continue;
            }
            String simpleName = field.getType().getSimpleName().toLowerCase();

            // 数值类型
            List<String> numberTypes = Arrays.asList(double.class.getSimpleName(), float.class.getSimpleName(), int.class.getSimpleName(), Integer.class.getSimpleName().toLowerCase(), BigDecimal.class.getSimpleName().toLowerCase(), long.class.getSimpleName().toLowerCase());
            if (numberTypes.contains(simpleName)) {
                ew.eq(StrUtil.toUnderlineCase(name), value);
            }

//            字符串类型
            if (String.class.getSimpleName().equalsIgnoreCase(simpleName)) {
                if (StringUtils.isBlank((String) value)) {
                    continue;
                }
                ew.like(StrUtil.toUnderlineCase(name), value);
            }
//            其他类型：略
        }
        return ew;
    }

    /**
     * 详情信息，用String接收，通用各种key类型
     */
    @ApiOperation("信息")
    @GetMapping("/info/{id}")
    public R info(@PathVariable("id") String id) {

        T entity = service.getById(id);
        return R.ok().put("entity", entity);
    }

    /**
     * 保存
     */
    @ApiOperation("保存")
    @SysLog("保存功能")
    @PostMapping("/save")
    @RequiresAction("save")
    public R save(@RequestBody T entity) {
        ValidatorUtils.validateEntity(entity);
        service.save(entity);
        return R.ok().put("entity", entity);
    }

    /**
     * 更新
     *
     * @param entity
     * @return
     */
    @ApiOperation("更新")
    @SysLog("更新功能")
    @PostMapping("/update")
    @RequiresAction("update")
    public R update(@RequestBody T entity) {

        ValidatorUtils.validateEntity(entity);
        //全部更新
        service.updateById(entity);
        return R.ok().put("entity", service.getById(entity.getId()));
    }

    /**
     * @Author: JuiceYan
     * @Description: 用String接收，通用各种key类型
     * @param: [ids, t]
     * @return: com.jy.framework.utils.R
     * @throws:
     * @Date: 4:02 2018/12/23
     */
    @ApiOperation("删除")
    @SysLog("删除功能")
    @PostMapping("/delete")
    @RequiresAction("delete")
    public R delete(@RequestBody String[] ids) {
        service.removeByIds(Arrays.asList(ids));

        return R.ok();
    }
//    @ApiOperation("导出")
    @SysLog("数据导出")
    @GetMapping("exportExcel")
    @RequiresAction("exportExcel")
    public R exportExcel(ModelMap map, HttpServletRequest request,
                         HttpServletResponse response) {
        Class<T> entityClass = getSuperclassType(this, ENTITY_INDEX);

        ExportParams params = new ExportParams("excel数据", "1", ExcelType.XSSF);
        params.setFreezeCol(2);
        Wrapper<T> wrapper = new QueryWrapper<>();
//                添加通用条件
        List<T> list = service.list(wrapper);
        map.put(NormalExcelConstants.DATA_LIST, list);
        map.put(NormalExcelConstants.CLASS, entityClass);
        map.put(NormalExcelConstants.PARAMS, params);
        PoiBaseView.render(map, request, response, NormalExcelConstants.EASYPOI_EXCEL_VIEW);
        return R.ok();
    }
//    @ApiOperation("模板")
    @GetMapping("template")
    @RequiresAction("importExcel")
    public R template(ModelMap map, HttpServletRequest request,
                      HttpServletResponse response) {
        Class<T> entityClass = getSuperclassType(this, ENTITY_INDEX);

        ExportParams params = new ExportParams("template", "1", ExcelType.XSSF);
        params.setFreezeCol(2);
//                添加通用条件
        List<T> list = new ArrayList<>();
        map.put(NormalExcelConstants.DATA_LIST, list);
        map.put(NormalExcelConstants.CLASS, entityClass);
        map.put(NormalExcelConstants.PARAMS, params);
        PoiBaseView.render(map, request, response, NormalExcelConstants.EASYPOI_EXCEL_VIEW);
        return R.ok();
    }
    @ApiOperation("导入")
    @SysLog("数据导入")
    @PostMapping("importExcel")
    @RequiresAction("importExcel")
    public R importExcel(@RequestParam("file") MultipartFile file, HttpServletRequest request,
                         HttpServletResponse response) throws Exception {
        Class<T> entityClass = getSuperclassType(this, ENTITY_INDEX);
        ImportParams params = new ImportParams();
        params.setNeedVerfiy(true);
        params.setTitleRows(1);
        params.setHeadRows(1);
//        paramsStr.setNeedCheckOrder(true);
//        paramsStr.setKeyIndex(1);
        ExcelImportResult<T> result = ExcelImportUtil.importExcelMore(file.getInputStream(),
                entityClass, params);
        log.error("失败条数-" + result.getFailList().size());
        log.error("失败数据-" + result.getFailList());
        List<T> list = result.getList();
        if (CollectionUtils.isEmpty(list)) {
            throw new MyException("请在excel文件中添加对应数据");
        }
        service.saveBatch(list);
        return R.ok("本次上传了" + list.size() + "条数据！");
    }
// @ApiIgnore
//    @SysLog("数据导入")
//    @RequestMapping("importExcel")
//    @RequiresAction("importExcel")
//    public void importExcel(@RequestParam("file") MultipartFile file, HttpServletRequest request,
//                            HttpServletResponse response) throws Exception {
//        Class<T> entityClass = getSuperclassType(this, ENTITY_INDEX);
//        ImportParams params = new ImportParams();
//        params.setNeedVerfiy(true);
//        params.setTitleRows(1);
//        params.setHeadRows(1);
////        paramsStr.setNeedCheckOrder(true);
////        paramsStr.setKeyIndex(1);
//        ExcelImportResult<T> result = ExcelImportUtil.importExcelMore(file.getInputStream(),
//                entityClass, params);
//        log.error("失败条数-" + result.getFailList().size());
//        log.error("失败数据-" + result.getFailList());
//        List<T> list = result.getList();
//        if (CollectionUtils.isEmpty(list)) {
//            return;
//        }
////        todo
//        list.stream().forEach(entity -> service.save(entity));
//        String url = request.getRequestURI();
//        String basePath = url.substring(0, url.indexOf("/", 1));
//        String pageName = entityClass.getSimpleName().replace("Entity", "").toLowerCase();
//
//        response.sendRedirect("/index.html#modules" + basePath + "/" + pageName + ".html");
//        //        return service.saveBatch(list);
//    }

    public static <T> Class<T> getSuperclassType(Object obj, int i) {
        //先得到类的字节码
        Class<?> aClass = obj.getClass();
        Type genericSuperclass = aClass.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType types = (ParameterizedType) genericSuperclass;//抽象类
            Type[] actualTypeArguments = types.getActualTypeArguments();
            Class<T> reponseClass = (Class) actualTypeArguments[i];
            return reponseClass;
        } else {
            return null;
        }
    }

    /**
     * 包装一个list，让list增加额外属性
     */
    protected <TT> Object warpObject(TT list, Consumer<? super Map<String, Object>> wrap) {
        Object warp = BaseControllerWarpper.warp(list, wrap);
        System.out.println(warp);
        return warp;
    }

    /**
     * @Author: JuiceYan
     * @Description: 获取用户信息
     * @param: []
     * @return: com.jy.framework.sys.entity.SysUserEntity
     * @throws:
     * @Date: 7:56 2018/12/22
     */
    protected SysUserEntity getUser() {
        SysUserEntity user = ShiroUtils.getUserEntity();
        return user;
    }

    /**
     * @Author: JuiceYan
     * @Description: 获取用户id
     * @param: []
     * @return: java.lang.Long
     * @throws:
     * @Date: 7:57 2018/12/22
     */
    protected Long getUserId() {

        return getUser().getId();
    }

    /**
     * @Author: JuiceYan
     * @Description: 获取部门id
     * @param: []
     * @return: java.lang.Long
     * @throws:
     * @Date: 7:57 2018/12/22
     */
    protected Long getDeptId() {
        return getUser().getDeptId();
    }


    /**
     * 返回前台文件流
     *
     * @author fengshuonan
     * @date 2017年2月28日 下午2:53:19
     */
    protected ResponseEntity<byte[]> renderFile(String fileName, String filePath) {
        byte[] bytes = FileUtil.toByteArray(filePath);
        return renderFile(fileName, bytes);
    }

    /**
     * 返回前台文件流
     *
     * @author fengshuonan
     * @date 2017年2月28日 下午2:53:19
     */
    protected ResponseEntity<byte[]> renderFile(String fileName, byte[] fileBytes) {
        String dfileName = null;
        try {
            dfileName = new String(fileName.getBytes("gb2312"), "iso8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", dfileName);
        return new ResponseEntity<byte[]>(fileBytes, headers, HttpStatus.CREATED);
    }


    /**
     * @Author: JuiceYan
     * @Description: 字段排序问题修复，默认按更新时间升序
     * @param: [params, ew]
     * @return: void
     * @throws:
     * @Date: 11:14 2019/1/7
     */
    public static <T extends BaseEntity> void buildOrderWrapper(Map<String, Object> params, QueryWrapper<T> ew) {
        String order = (String) params.get("order");
        boolean isAsc = StringUtils.equalsIgnoreCase(order, "asc");

        String sidx = (String) params.get("sidx");
        boolean sidxFlag = StringUtils.isNotBlank(sidx);
//      增强兼容性，如果是驼峰形式，转为下划线
        if (sidxFlag) {
            sidx = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, sidx);
            ew.orderBy(sidxFlag, isAsc, sidx);
        } else {
            ew.lambda().orderByDesc(BaseEntity::getUpdateTime,BaseEntity::getCreateTime);
        }
    }
}
